
/*
 * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * (b)  If this Sample Code is distributed as part of the Display PostScript
 *	System Software Development Kit from Adobe Systems Incorporated,
 *	then this copy is designated as Development Software and its use is
 *	subject to the terms of the License Agreement attached to such Kit.
 *
 * (c)  If this Sample Code is distributed independently, then the following
 *	terms apply:
 *
 * (d)  This file may be freely copied and redistributed as long as:
 *	1) Parts (a), (d), (e) and (f) continue to be included in the file,
 *	2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
 *	Adobe Systems Incorporated.
 * 
 * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
 *	CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
 *	AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
 *	ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
 *	OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
 *	WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
 *	WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
 *	DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 *	FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
 *	OF THIRD PARTY RIGHTS.
 */

/*
 *	DialView.m
 *	Created by Ken Fromm
 *
 *	This application draws hash marks on a dial in several ways. The first way
 *	draws using wraps and the rotate operator. The second draws with wraps
 *	but calculates the trig for the beginning and ending points of the line segments.
 *	The third calculates the trig and sends the line segments to the server with
 *	user paths. This is the fastest drawing approach and is recommended
 *	for all but the simplest drawing. The last method stores the user path in the
 *	server instead of calculating the points anew. This is may be useful in a few
 *	cases where the paths are large, imaged frequently and do not change.
 *	In most cases, though, the overhead of managing paths in the server
 *	overrides any savings.
 *
 *	Version:	2.0
 *	Author:	Ken Fromm
 *	History:
 *			03-07-91		Added this comment.
 *			05-10-91		Simplified the application from six methods to
 *						four. (The two taken out were variations of 
 *						user path drawing and tended to cloud the
 *						message of the application.)
 */

#import "DialView.h"
#import "DialViewWraps.h"
#import <appkit/Button.h>
#import <appkit/Control.h>
#import <appkit/Matrix.h>
#import <appkit/TextField.h>
#import <appkit/nextstd.h>
#import <math.h>
#import <dpsclient/wraps.h>

@implementation DialView

/*
*	These definitions are for the names of the user path arrays in the server.  Providing
*	names like this is only to simplify the example. An optimized solution would probably use
*	user objects and forego names altogether.
*/
static char *upath1 = "upath1";
static char *upath10 = "upath10";
static char *upath45 = "upath45";
static char *upath90 = "upath90";

/*
*	Allocate a gstate and set clipping to NO.  The gstate is allocated because we do not
*	want to reset the individual drawing variables when the context is switched back to this
*	view.  The clipping is set to NO because the drawing methods do not need clipping so
*	its a way to save display time.
*
*	The dimensions of the dial are calculated. The bounds of the DialView is the starting
*	point. Arrays to hold user path point and operator values are allocated.
*
*	The PSWDefs() call sends some definitions used in the wraps to the interpreter.  They
*	will be available in the interpreter when the wraps call them. 
*/

+ newFrame:(NXRect *) frm
{
	self = [super newFrame:frm]; 
	[self allocateGState]; 
	[self setClipping:NO];

	viewcenter.x =  bounds.size.width/2;
	viewcenter.y = bounds.size.height/2;
	if (viewcenter.x > viewcenter.y)
		maxdim = viewcenter.y - WIDCIRCBRD/2 - CIRCFF ;
	else
		maxdim = viewcenter.x - WIDCIRCBRD/2 -CIRCFF;
	viewcenter.x += bounds.origin.x;
	viewcenter.y += bounds.origin.y;

	NX_MALLOC(pts, float, MAX_PTS); 
	NX_MALLOC(ops, char, MAX_OPS);
	
	PSWDefs ();
	[self setupUpaths];

	drawFlags.field = 0x80;

	return self;
}

/* Free the arrays upon quitting. */
- free
{
	if (pts)
		NX_FREE(pts);
	if (ops)
		NX_FREE(ops);
  
   	return [super free];
}

/*  Created by interface.  Used to obtain the id's of objects that will be messaged. */
- setMatrixDegreeTypes:anObject
{
	matrixDegreeTypes = anObject;
	return self;
}

- setMatrixDisplayTimes:anObject
{
	matrixDisplayTimes = anObject;
	return self;
}

/*
*	Calculate the start and end points and place in user path format.
*	Send the user path to the server and define it in the server. Execute
*	the user path for drawing.
*/
static void setupUpathTrig (pts, ops, x, y, startlen, endlen, deg, upathname, bbox)
	float		pts[];
	char		ops[];
	float 	x, y, startlen, endlen, deg;
	char		*upathname;
	float		bbox[4];
{
	int		i , j, angle;
	
	i = j = 0;
	for (angle = 0; angle < 360; angle += deg)
	{
		pts[i++] = x + (float) cos(angle * RADIANS) * startlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * startlen;
		ops[j++] = dps_moveto;
		
		pts[i++] = x + (float) cos(angle * RADIANS) * endlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * endlen;
		ops[j++] = dps_lineto;
	}

	PSWPlaceName(upathname);
	DPSDoUserPath(pts, i, dps_float, ops, j, bbox, dps_def);
}

- setupUpaths
{
	float		bbox[4];
	
	bbox[0] = bounds.origin.x;
	bbox[1] = bounds.origin.y;
	bbox[2] = bounds.origin.x + bounds.size.width;
	bbox[3] = bounds.origin.y + bounds.size.height;
		
	setupUpathTrig(pts, ops, viewcenter.x, viewcenter.y,
				maxdim * LEN1, maxdim, DEG1, upath1, bbox);
	setupUpathTrig(pts, ops, viewcenter.x, viewcenter.y,
				maxdim * LEN10, maxdim, DEG10, upath10, bbox);
	setupUpathTrig(pts, ops, viewcenter.x, viewcenter.y,
				maxdim * LEN45, maxdim, DEG45, upath45, bbox);
	setupUpathTrig(pts, ops, viewcenter.x, viewcenter.y,
				maxdim * LEN90, maxdim, DEG90, upath90, bbox);

	return self;
}

/*
*	This method changes the title of the menu cell according to the
* 	value of the trace variable.
*/
-trace:sender
{
	if (trace == NO)
		[[sender selectedCell] setTitle:"Trace On"];
	else
		[[sender selectedCell] setTitle:"Trace Off"];
	trace = !trace;
	
	return self;
}

- eraseTimes:sender
{
	int		i;
	
	for (i = 0; i < [matrixDisplayTimes cellCount]; i++)
		[[matrixDisplayTimes cellAt:i :0] setStringValue:""];

	return self;
}

-drawViewOne:sender
{
	drawFlags.field = 0x80 >> [sender  selectedRow];
	[self display];

	return self;
}

-drawViewAll:sender
{
	drawFlags.field = DRAWALL;
	[self display];

	return self;
}

/* Use the same line dimensions except rotate the graphics state before each line. */
static void drawWrapsRotate (clr, wid, startlen, endlen, deg)
	float 	clr, wid, startlen, endlen, deg;
{
	int		angle;
	
	PSWSetColorWidth(clr, wid);
	for (angle = 0; angle < 360; angle += deg)
		PSWRotate_MakeLine(deg, startlen, 0, endlen, 0);
	PSstroke();
}

- drawWrapsRotate:(int) cell 
{
	int			ElapsedTime;

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);

	PSWMarkTime (); NXPing ();	
		if (trace)
			DPSTraceContext(DPSGetCurrentContext(), YES);
		PSgsave();
			PStranslate(viewcenter.x, viewcenter.y);			
			if ([[matrixDegreeTypes cellAt:1 :1] state])				drawWrapsRotate(CLR1, WID1, maxdim * LEN1, maxdim, DEG1);
			if ([[matrixDegreeTypes cellAt:0 :1] state])				drawWrapsRotate(CLR10, WID10, maxdim * LEN10, maxdim, DEG10);
			if ([[matrixDegreeTypes cellAt:1 :0] state])				drawWrapsRotate(CLR45, WID45, maxdim * LEN45, maxdim, DEG45);
			if ([[matrixDegreeTypes cellAt:0 :0] state])				drawWrapsRotate(CLR90, WID90, maxdim * LEN90, maxdim, DEG90);
		PSgrestore();
		if (trace)
			DPSTraceContext(DPSGetCurrentContext(), NO);
	PSWReturnTime (&ElapsedTime);

	PSWSetColorWidth(CLRCIRCBRD, WIDCIRCBRD);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSstroke();
			
	[[matrixDisplayTimes cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/*
*	Calculate the position of the start and end points and add the line to the
*	current path.  Not bad for this particular object but the calculations could
*	become ugly for something more complex.
*/
static void drawWrapsTrig (clr, wid, x, y, startlen, endlen, deg)
	float 	clr, wid, x, y, startlen, endlen, deg;
{
	int		angle;
	
	PSWSetColorWidth(clr, wid);
	for (angle = 0; angle < 360; angle += deg)
		PSWMakeLine(x + (float) cos(angle * RADIANS) * startlen,
				y + (float) sin(angle * RADIANS) * startlen,
				x + (float) cos(angle * RADIANS) * endlen,
				y + (float) sin(angle * RADIANS) * endlen);
	PSstroke();
}

- drawWrapsTrig:(int) cell
{
	int			ElapsedTime;

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);

	PSWMarkTime ();  NXPing ();	
		if (trace)
			DPSTraceContext(DPSGetCurrentContext(), YES);
		if ([[matrixDegreeTypes cellAt:1 :1] state])			drawWrapsTrig(CLR1, WID1, viewcenter.x, viewcenter.y, 
				maxdim * LEN1, maxdim, DEG1);
		if ([[matrixDegreeTypes cellAt:0 :1] state])			drawWrapsTrig(CLR10, WID10, viewcenter.x, viewcenter.y, 
				maxdim * LEN10, maxdim, DEG10);
		if ([[matrixDegreeTypes cellAt:1 :0] state])			drawWrapsTrig(CLR45, WID45, viewcenter.x, viewcenter.y, 
				maxdim * LEN45, maxdim, DEG45);
		if ([[matrixDegreeTypes cellAt:0 :0] state])			drawWrapsTrig(CLR90, WID90, viewcenter.x, viewcenter.y, 
				maxdim * LEN90, maxdim, DEG90);
		if (trace)
			DPSTraceContext(DPSGetCurrentContext(), NO);
	PSWReturnTime (&ElapsedTime);

	PSWSetColorWidth(CLRCIRCBRD, WIDCIRCBRD);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSstroke();
					
	[[matrixDisplayTimes cellAt:cell :0] setIntValue:ElapsedTime];

	return self;
}

/*
*	Calculate the start and end points and place in user path format.
*	Send the entire user path at once using the DPSDoUserPath() call.
*/
static void drawUpathsTrig (pts, ops, clr, wid, x, y, startlen, endlen, deg, bbox)
	float		pts[];
	char		ops[];
	float 	clr, wid, x, y, startlen, endlen, deg;
	float		bbox[4];
{
	int		i , j, angle;
	
	i = j = 0;
	for (angle = 0; angle < 360; angle += deg)
	{
		pts[i++] = x + (float) cos(angle * RADIANS) * startlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * startlen;
		ops[j++] = dps_moveto;
		
		pts[i++] = x + (float) cos(angle * RADIANS) * endlen;
		pts[i++] = y + (float) sin(angle * RADIANS) * endlen;
		ops[j++] = dps_lineto;
	}

	PSWSetColorWidth(clr, wid);
	DPSDoUserPath(pts, i, dps_float, ops, j, bbox, dps_ustroke);
}

- drawUpathsTrig:(int) cell;
{
	int			ElapsedTime;

	float		bbox[4];

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);
		
	PSWMarkTime (); NXPing ();		
		bbox[0] = bounds.origin.x;
		bbox[1] = bounds.origin.y;
		bbox[2] = bounds.origin.x + bounds.size.width;
		bbox[3] = bounds.origin.y + bounds.size.height;

		if (trace)
			DPSTraceContext(DPSGetCurrentContext(), YES);
		if ([[matrixDegreeTypes cellAt:1 :1] state])			drawUpathsTrig(pts, ops, CLR1, WID1, viewcenter.x, viewcenter.y,
				maxdim * LEN1, maxdim, DEG1, bbox);
		if ([[matrixDegreeTypes cellAt:0 :1] state])			drawUpathsTrig(pts, ops, CLR10, WID10, viewcenter.x, viewcenter.y,
				maxdim * LEN10, maxdim, DEG10, bbox);
		if ([[matrixDegreeTypes cellAt:1 :0] state])			drawUpathsTrig(pts, ops, CLR45, WID45, viewcenter.x, viewcenter.y,
				maxdim * LEN45, maxdim, DEG45, bbox);
		if ([[matrixDegreeTypes cellAt:0 :0] state])			drawUpathsTrig(pts, ops, CLR90, WID90, viewcenter.x, viewcenter.y,
				maxdim * LEN90, maxdim, DEG90, bbox);
		if (trace)
			DPSTraceContext(DPSGetCurrentContext(), NO);
	PSWReturnTime (&ElapsedTime);

	PSWSetColorWidth(CLRCIRCBRD, WIDCIRCBRD);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSstroke();
		
	[[matrixDisplayTimes cellAt:cell  :0] setIntValue:ElapsedTime];

	return self;
}

- drawUpathsServer:(int) cell
{
	int			ElapsedTime;

	[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSWFillPath(CLRCIRC);
		
	PSWMarkTime (); NXPing ();		
		if (trace)
			DPSTraceContext(DPSGetCurrentContext(), YES);
		if ([[matrixDegreeTypes cellAt:1 :1] state])		{
			PSWSetColorWidth(CLR1, WID1);
			PSWDrawUserPath(upath1);
		}
		if ([[matrixDegreeTypes cellAt:0 :1] state])		{
			PSWSetColorWidth(CLR10, WID10);
			PSWDrawUserPath(upath10);
		}
		if ([[matrixDegreeTypes cellAt:1 :0] state])		{
			PSWSetColorWidth(CLR45, WID45);
			PSWDrawUserPath(upath45);
		}
		if ([[matrixDegreeTypes cellAt:0 :0] state])
		{
			PSWSetColorWidth(CLR90, WID90);
			PSWDrawUserPath(upath90);
		}
		if (trace)
			DPSTraceContext(DPSGetCurrentContext(), NO);
	PSWReturnTime (&ElapsedTime);

	PSWSetColorWidth(CLRCIRCBRD, WIDCIRCBRD);
	PSWMakeCircle(viewcenter.x, viewcenter.y, maxdim);
	PSstroke();
		
	[[matrixDisplayTimes cellAt:cell  :0] setIntValue:ElapsedTime];

	return self;
}

- drawSelf:(NXRect *)r :(int) count
{
	PSWEraseView (CLRVIEW, bounds.origin.x, bounds.origin.y,
					bounds.size.width, bounds.size.height);

	if (drawFlags.flags.wrapsrotate)
		[self drawWrapsRotate:0];
	if (drawFlags.flags.wrapstrig)
		[self drawWrapsTrig:1];
	if (drawFlags.flags.upathstrig)
		[self drawUpathsTrig:2];
	if (drawFlags.flags.upathsserver)
		[self drawUpathsServer:3];

	return self;
}

@end
